/*!
\copyright  Copyright (c) 2021 - 2022 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      The LE voice source interface implementation for Mirror Profile
*/

#ifdef ENABLE_LEA_CIS_DELEGATION

#include "mirror_profile_private.h"
#include "mirror_profile_le_voice_source.h"
#include "source_param_types.h"
#include "voice_sources.h"
#include "telephony_messages.h"
#include "le_audio_messages.h"
#include "kymera.h"

#include <stdlib.h>
#include <panic.h>

static bool mirrorProfile_GetLeVoiceConnectParameters(voice_source_t source, source_defined_params_t * source_params);
static void mirrorProfile_FreeLeVoiceConnectParameters(voice_source_t source, source_defined_params_t * source_params);
static bool mirrorProfile_GetLeVoiceDisconnectParameters(voice_source_t source, source_defined_params_t * source_params);
static void mirrorProfile_FreeLeVoiceDisconnectParameters(voice_source_t source, source_defined_params_t * source_params);
static bool mirrorProfile_IsLeVoiceAvailable(voice_source_t source);
static unsigned mirrorProfile_GetLeVoiceCurrentContext(voice_source_t source);

static const voice_source_audio_interface_t mirror_le_voice_interface =
{
    .GetConnectParameters = mirrorProfile_GetLeVoiceConnectParameters,
    .ReleaseConnectParameters = mirrorProfile_FreeLeVoiceConnectParameters,
    .GetDisconnectParameters = mirrorProfile_GetLeVoiceDisconnectParameters,
    .ReleaseDisconnectParameters = mirrorProfile_FreeLeVoiceDisconnectParameters,
    .IsAudioRouted = mirrorProfile_IsLeVoiceAvailable,
    .IsVoiceChannelAvailable = mirrorProfile_IsLeVoiceAvailable,
    .SetState = NULL,
    .GetState = NULL
};

static const voice_source_telephony_control_interface_t mirror_le_telephony_interface =
{
    .GetUiProviderContext = mirrorProfile_GetLeVoiceCurrentContext
};

static void mirrorProfile_PopulateStreamConfig(le_voice_config_t *le_voice_config)
{
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();

    le_voice_config->speaker_stream.frame_length = lea_unicast->audio_config.sink_frame_length;
    le_voice_config->speaker_stream.stream_type = KYMERA_LE_STREAM_MONO;
    le_voice_config->speaker_stream.codec_frame_blocks_per_sdu = lea_unicast->audio_config.sink_blocks_per_sdu;
    le_voice_config->speaker_stream.presentation_delay = lea_unicast->audio_config.sink_presentation_delay;

    le_voice_config->mic_stream.frame_length = lea_unicast->audio_config.source_frame_length;
    le_voice_config->mic_stream.stream_type = KYMERA_LE_STREAM_MONO;
    le_voice_config->mic_stream.codec_frame_blocks_per_sdu = lea_unicast->audio_config.source_blocks_per_sdu;
    le_voice_config->mic_stream.presentation_delay = lea_unicast->audio_config.source_presentation_delay;
}

/* \brief Extract voice connect parameters from stored mirror unicast state Information */
static bool mirrorProfile_ExtractLeVoiceParameters(le_voice_connect_parameters_t *conn_param)
{
    bool populate_success = FALSE;
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();

    DEBUG_LOG("mirrorProfile_ExtractLeVoiceParameters :");

    /* Fill in the voice Parameters */
    if (lea_unicast->audio_config.voice_source != voice_source_none)
    {
        conn_param->volume = VoiceSources_CalculateOutputVolume(voice_source_le_audio_unicast);
        conn_param->le_voice_config.mic_mute_state = lea_unicast->audio_config.mic_mute_state;
        conn_param->le_voice_config.source_iso_handle = lea_unicast->own_cis_handle;
        conn_param->le_voice_config.source_iso_handle_right = LE_AUDIO_INVALID_ISO_HANDLE;
        conn_param->le_voice_config.sink_iso_handle = MirrorProfile_IsSourceSinkDifferentCis() ? lea_unicast->peer_cis_handle : lea_unicast->own_cis_handle;
        conn_param->le_voice_config.sample_rate = lea_unicast->audio_config.sink_sampling_frequency;
        conn_param->le_voice_config.frame_duration = lea_unicast->audio_config.sink_frame_duration;
        conn_param->le_voice_config.codec_type = lea_unicast->audio_config.sink_codec_id;
        conn_param->le_voice_config.codec_version = lea_unicast->audio_config.codec_version;

        mirrorProfile_PopulateStreamConfig(&conn_param->le_voice_config);
        populate_success = TRUE;

        DEBUG_LOG(" source_iso_handle : %d, source_iso_handle_right : %d, sink_iso_handle : %d, Volume %d",
                  conn_param->le_voice_config.source_iso_handle,
                  conn_param->le_voice_config.source_iso_handle_right,
                  conn_param->le_voice_config.sink_iso_handle,
                  conn_param->volume.value);

        DEBUG_LOG(" sample_rate : %d, frame_duration : %d, codec_type %d, codec_version %d",
                  conn_param->le_voice_config.sample_rate,
                  conn_param->le_voice_config.frame_duration,
                  conn_param->le_voice_config.codec_type,
                  conn_param->le_voice_config.codec_version);
    }

    return  populate_success;
}

static bool mirrorProfile_GetLeVoiceConnectParameters(voice_source_t source, source_defined_params_t * source_params)
{
    bool populate_success = FALSE;

    DEBUG_LOG("mirrorProfile_GetLeVoiceConnectParameters");

    if (source == voice_source_le_audio_unicast)
    {
        le_voice_connect_parameters_t *conn_param =
            (le_voice_connect_parameters_t *) PanicUnlessMalloc(sizeof(le_voice_connect_parameters_t));

        if (mirrorProfile_ExtractLeVoiceParameters(conn_param))
        {
            source_params->data = (void *) conn_param;
            source_params->data_length = sizeof(le_voice_connect_parameters_t);
            populate_success = TRUE;
        }
        else
        {
            /* Free the allocated voice connect parameter */
            pfree(conn_param);
        }
    }

    DEBUG_LOG("mirrorProfile_GetLeVoiceConnectParameters success %d", populate_success);

    return populate_success;
}

static void mirrorProfile_FreeLeVoiceConnectParameters(voice_source_t source, source_defined_params_t * source_params)
{
    UNUSED(source);

    pfree(source_params->data);
    source_params->data = (void *) NULL;
    source_params->data_length = 0;
}

static bool mirrorProfile_GetLeVoiceDisconnectParameters(voice_source_t source, source_defined_params_t * source_params)
{
    UNUSED(source);

    PanicNull(source_params);
    source_params->data = (void *) NULL;
    source_params->data_length = 0;

    return TRUE;
}

static void mirrorProfile_FreeLeVoiceDisconnectParameters(voice_source_t source, source_defined_params_t * source_params)
{
    UNUSED(source);

    PanicNull(source_params);
    source_params->data = (void *) NULL;
    source_params->data_length = 0;
}

static bool mirrorProfile_IsLeVoiceAvailable(voice_source_t source)
{
    bool is_available = FALSE;
    voice_source_t voice_source = MirrorProfile_GetLeVoiceSource();

    DEBUG_LOG("mirrorProfile_IsLeVoiceAvailable source=%d Mirror state = %d", source, MirrorProfile_GetState());

    if(voice_source == source && MirrorProfile_IsSecondary())
    {
        switch (MirrorProfile_GetState())
        {
            case MIRROR_PROFILE_STATE_CIS_CONNECTED:
                is_available = MirrorProfile_GetLeaUnicastState()->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED;
            break;
            default:
            break;
        }
    }

    return is_available;
}

static unsigned mirrorProfile_GetLeVoiceCurrentContext(voice_source_t source)
{
    voice_source_provider_context_t context = context_voice_disconnected;

    if (mirrorProfile_IsLeVoiceAvailable(source))
    {
        context = context_voice_in_call;
    }
    return context;
}

const voice_source_audio_interface_t * MirrorProfile_GetLeVoiceInterface(void)
{
    return &mirror_le_voice_interface;
}

const voice_source_telephony_control_interface_t * MirrorProfile_GetLeTelephonyControlInterface(void)
{
    return &mirror_le_telephony_interface;
}

void MirrorProfile_StartLeVoice(void)
{
    voice_source_t voice_source = MirrorProfile_GetLeVoiceSource();

    LeAudioMessages_SendUnicastVoiceConnected(voice_source_le_audio_unicast);

    Telephony_NotifyCallAudioConnected(voice_source);
}

void MirrorProfile_StopLeVoice(void)
{
    voice_source_t voice_source = MirrorProfile_GetLeVoiceSource();

    LeAudioMessages_SendUnicastVoiceDisconnected(voice_source_le_audio_unicast);

    Telephony_NotifyCallAudioDisconnected(voice_source);
}

#endif /* ENABLE_LEA_CIS_DELEGATION */

